/*
    Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <windows.h>
#include "Scheduler.h"
#include <algorithm>
#include "Debuger.h"

namespace SkyEngine2d
{

    class Scheduler::PerFramScheduler
    {

        SchedulerPerFramFunc func;
        int priority;
        std::string key;
        bool paused;
        bool aborted;
        friend class Scheduler;

    public:
        PerFramScheduler(const SchedulerPerFramFunc &scheduler_func, int priority, const std::string &key, bool paused)
            : func(scheduler_func), priority(priority), key(key), paused(paused), aborted(false)
        {
        }
        ~PerFramScheduler() {}

        bool isAborted() const { return aborted; }
        const std::string &getKey() const { return key; };
        void setPaused(bool b = false) { paused = b; }
    };

    class Scheduler::SchedulerTask
    {
    public:
        SchedulerTask(const SchedulerTaskFunc &func, int repeat, int64_t interval, bool paused = false)
            : task(func), repeat(repeat), interval(interval), paused(paused), aborted(false)
        {
            isRnuForever = repeat == 0;
            nextTime = Scheduler::getInstance()->m_currTime + interval;
        }

    private:
        SchedulerTaskFunc task;
        bool paused;
        bool isRnuForever;
        uint32_t repeat; //重复的次数
        int64_t interval;
        int64_t nextTime; //下次触发的时间
        bool aborted;
        friend class Scheduler;
    };

    Scheduler::Scheduler()
        : m_perSecondCount(0), m_currTime(0)
    {
        LARGE_INTEGER freq;
        QueryPerformanceFrequency(&freq);
        m_perSecondCount = freq.QuadPart;
    }

    Scheduler::~Scheduler()
    {
    }

    void Scheduler::update(double dt)
    {
        bool has_except = false;
        SchedulerExcept scheduler_e("有异常抛出");

        //处理被删除的调度器
        typedef std::unique_ptr<Scheduler::PerFramScheduler> perframPtr;

        auto i = std::remove_if(m_perFrameSchduler.begin(), m_perFrameSchduler.end(),
                                [](const perframPtr &a)
                                { return a->isAborted(); });

        if (i != m_perFrameSchduler.end())
        {
            m_perFrameSchduler.erase(i);
        }

        //先进行每帧调用的处理
        //优先级高的先处理 ， 默认优先级为 0
        for (const auto &per : m_perFrameSchduler)
        {
            if (!per->aborted && !per->paused)
            {
                per->func(float(dt));
            }
        }

        //调用定时调度器
        m_currTime += dt * m_perSecondCount;

        auto iter = m_taskSchduler.begin();
        for (; iter != m_taskSchduler.end();)
        {
            std::shared_ptr<SchedulerTask> task = iter->second;
            if (!task->aborted && !task->paused && task->nextTime <= m_currTime)
            {
                //执行func
                try
                {
                    task->task();
                }
                catch (Except &e)
                {
                    has_except = true;
                    scheduler_e.setLastExcept(e);
                }

                if (!task->isRnuForever) //如果不是一直运行
                {
                    --task->repeat;
                    if (task->repeat <= 0)
                    {
                        iter = m_taskSchduler.erase(iter);
                        continue;
                    }
                }

                //更新下一次的时间
                task->nextTime = m_currTime + task->interval;
            }

            iter++;
        }

        if (has_except)
        {
            throw scheduler_e;
        }
    }

    void Scheduler::addSchedulerPerFrame(const SchedulerPerFramFunc &scheduler_func, const std::string &key, int priority, bool paused /*= false*/)
    {
        m_perFrameSchduler.push_back(std::make_unique<Scheduler::PerFramScheduler>(scheduler_func, priority, key, paused));
        typedef std::unique_ptr<Scheduler::PerFramScheduler> T;
        //按优先级排序
        m_perFrameSchduler.sort([](const T &a, const T &b)
                                { return a->priority > b->priority; });
    }

    void Scheduler::addSchedulerTaskDelay(const SchedulerTaskFunc &scheduler_task, const std::string &key, int delay /*=0*/)
    {
        const auto &f = m_taskSchduler.find(key);
        if (f != m_taskSchduler.end())
        {
            DebugWarning("addSchedulerTask  key 冲突");
            return;
        }

        int64_t i = (delay / 1000.0f) * m_perSecondCount;
        m_taskSchduler.emplace(key, std::make_unique<Scheduler::SchedulerTask>(scheduler_task, 1, i));
    }

    void Scheduler::addSchedulerTask(const SchedulerTaskFunc &scheduler_task, const std::string &key, int interval, int repeat, bool paused /*= false*/)
    {
        const auto &f = m_taskSchduler.find(key);
        if (f != m_taskSchduler.end())
        {
            DebugWarning("addSchedulerTask  key 冲突");
            return;
        }

        int64_t i = (interval / 1000.0f) * m_perSecondCount;
        m_taskSchduler.emplace(key, std::make_unique<Scheduler::SchedulerTask>(scheduler_task, repeat, i, paused));
    }

    void Scheduler::addSchedulerTaskForever(const SchedulerTaskFunc &scheduler_task, const std::string &key, int interval, bool paused /*= false*/)
    {
        const auto &f = m_taskSchduler.find(key);
        if (f != m_taskSchduler.end())
        {
            DebugWarning("addSchedulerTask  key 冲突");
            return;
        }

        int64_t i = (interval / 1000.0f) * m_perSecondCount;
        m_taskSchduler.emplace(key, std::make_unique<Scheduler::SchedulerTask>(scheduler_task, 0, i, paused));
    }

    void Scheduler::pausedSchedulerTask(const std::string &key) noexcept
    {
        auto &f = m_taskSchduler.find(key);
        if (f != m_taskSchduler.end())
        {
            f->second->paused = true;
        }
    }

    void Scheduler::pausedSchedulerPerFrame(const std::string &key) noexcept
    {
        auto &iter = m_perFrameSchduler.begin();
        for (; iter != m_perFrameSchduler.end(); iter++)
        {
            if ((*iter)->key == key)
            {
                (*iter)->paused = true;
                break;
            }
        }
    }

    void Scheduler::removeSchedulerPeerFrame(const std::string &key) noexcept
    {
        auto &iter = m_perFrameSchduler.begin();
        for (; iter != m_perFrameSchduler.end(); iter++)
        {
            if ((*iter)->key == key)
            {
                (*iter)->aborted = true;
                break;
            }
        }
    }

    void Scheduler::removeSchedulerTask(const std::string &key) noexcept
    {
        auto &f = m_taskSchduler.find(key);
        if (f != m_taskSchduler.end())
        {
            f->second->aborted = true;
            m_taskSchduler.erase(f);
        }
    }

    void Scheduler::resumeSchedulerTask(const std::string &key)
    {
        auto &f = m_taskSchduler.find(key);
        if (f != m_taskSchduler.end())
        {
            f->second->paused = false;
            return;
        }
        else
        {
            DebugWarning("resumeSchedulerTask key 不存在");
        }
    }

    void Scheduler::resumeSchedulerPerFrame(const std::string key)
    {
        auto &i = std::find_if(m_perFrameSchduler.begin(), m_perFrameSchduler.end(),
                               [&key](std::unique_ptr<Scheduler::PerFramScheduler> &p)
                               { return p->getKey() == key; });
        if (i != m_perFrameSchduler.end())
        {
            (*i)->paused = false;
            return;
        }
        else
        {
            DebugWarning("resumeSchedulerPerFrame key 不存在");
        }
    }

}
